#include "process.h"
#include <string.h> 
#include "kmm.h"
#include "kstdio.h"
#include "gdt.h"
#include "vmm.h"

extern "C" uint32_t __cdecl read_eip();

process_info_t *active_process;
uint32_t pid_alloc = 1;

void switch_to_usermode(){
	int data_segment = (4 * GDT_SELECTOR_MULTIPLIER) | RPL_USERMODE_MASK;
	short data_seg16 = (short) data_segment;
	int code_segment = (3 * GDT_SELECTOR_MULTIPLIER) | RPL_USERMODE_MASK;
	tss_set_stack(((uint32_t)active_process->kernel_stack)+KERNEL_STACK_SIZE);
	__asm{
		cli
		mov ax, data_seg16
		mov ds, ax
		mov es, ax
		mov fs, ax
		mov gs, ax
		mov eax, esp
		mov ebx, 0x23
		push ebx
		push eax
		pushfd
		pop eax
		or eax, 0x200
		push eax
		mov ebx, 0x1B
		push ebx
		push usermode
		iretd
	}
usermode:
	return;
}

void setup_initial_process(){
	active_process = (process_info_t *) kmalloc(sizeof(process_info_t));
	memset((void *)active_process,0,sizeof(process_info_t));
	active_process->pid = 0;
	active_process->root_directory = 0;//Until FS is running
	initialize_filetable(active_process);
	active_process->code_start  = (void *) 0xC0000000;
	active_process->kernel_stack = (void *)kmalloc(KERNEL_STACK_SIZE);
	strcpy(active_process->name,"System Process");
}

process_info_t * create_process(process_start_params_t *params){
	process_info_t * new_process = (process_info_t *) kmalloc(sizeof(process_info_t));
	memset((void *)new_process,0,sizeof(process_info_t));
	new_process->pid = pid_alloc++; //TODO: reuse killed pids
	new_process->uid = active_process->uid;
	new_process->gid = active_process->gid;
	new_process->parent = active_process;
	new_process->root_directory = active_process->root_directory;//Use parents root directory
	new_process->current_directory = active_process->current_directory;//Use parents working directory
	initialize_filetable(new_process);
	new_process->code_start  = (void *) params->image_base;
	new_process->heap_start  = (void *) (((params->image_base + params->code_size) + 0xFFF) & ~0xFFF);
	new_process->heap_end	 = (void *) (((uint32_t)new_process->heap_start) + params->initial_heap_size);
	new_process->stack_limit = (void *) (((((uint32_t)new_process->heap_start)+ params->max_heap_size)
		+ 0xFFF) & ~0xFFF) ;
	new_process->stack_base  = (void *) (((((uint32_t)new_process->stack_limit)+ params->max_stack_size)
		+ 0xFFF) & ~0xFFF);
	new_process->stack_start	 = (void *) (((((uint32_t)new_process->stack_base) - params->initial_stack_size)
		+ 0xFFF) & ~0xFFF);
	params->code_ptr = (uint32_t) new_process->code_start;
	new_process->kernel_stack = (void *)kmalloc(KERNEL_STACK_SIZE);
	vmm_alloc_area(new_process->code_start,new_process->heap_end,1,1);
	vmm_alloc_area(new_process->stack_start,new_process->stack_base,1,1);
	new_process->eip = params->image_base + params->entry_point;
	new_process->esp = ((uint32_t) new_process->stack_base)-4;
	return new_process;
}

void select_task(process_info_t *t){
	tss_set_stack(((uint32_t)t->kernel_stack)+KERNEL_STACK_SIZE);
	active_process = t;
}
int start_task(process_info_t *t){
	int data_segment = (4 * GDT_SELECTOR_MULTIPLIER) | RPL_USERMODE_MASK;
	short data_seg16 = (short) data_segment;
	int code_segment = (3 * GDT_SELECTOR_MULTIPLIER) | RPL_USERMODE_MASK;
	uint32_t cur_eip = 0;
	uint32_t cur_esp = 0;
	uint32_t cur_ebp = 0;
	uint32_t nt_eip = 0;
	uint32_t nt_esp = 0;
	uint32_t nt_ebp = 0;
	int retcode = 0;
	tss_set_stack(((uint32_t)t->kernel_stack)+KERNEL_STACK_SIZE);
	cur_eip = read_eip();
	if (cur_eip == 0x12345){
		__asm{
			mov retcode, edx
		}
		return retcode;
	}
	__asm {
		mov cur_esp, esp
		mov cur_ebp, ebp
	};
	active_process = t;
	t->exit_eip = cur_eip;
	t->exit_esp = cur_esp;
	t->exit_ebp = cur_ebp;
	nt_eip = t->eip;
	nt_esp = t->esp;
	nt_ebp = t->ebp;
	__asm{
		cli
		mov ax, data_seg16
		mov ds, ax
		mov es, ax
		mov fs, ax
		mov gs, ax
		mov eax, nt_esp
		mov ebx, 0x23
		push ebx
		push eax
		pushfd
		pop eax
		or eax, 0x200
		push eax
		mov ebx, 0x1B
		push ebx
		push nt_eip
		mov eax, nt_ebp
		mov ebp, eax
		iretd
	}
	return -1;//Never ever reached
}


process_info_t *get_current_process(){
	return active_process;
}

void exit(int code){
	uint32_t nt_eip;
	uint32_t nt_esp;
	uint32_t nt_ebp;
	nt_eip = active_process->exit_eip;
	nt_esp = active_process->exit_esp;
	nt_ebp = active_process->exit_ebp;
	active_process = active_process->parent;
	tss_set_stack(((uint32_t)active_process->kernel_stack)+KERNEL_STACK_SIZE);
	__asm {
		mov edx, code
		mov ecx, nt_eip
		mov ebx, nt_ebp
		mov esp, nt_esp
		mov ebp, ebx
		mov eax, 0x12345
		jmp ecx
	}
}